/*
    This file is part of Raptor.

    Raptor is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Raptor is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Raptor.  If not, see <http://www.gnu.org/licenses/>.

 */

#include "xmlFileLoader.h"

#include <QUrl>
#include <QRegExp>
#include <QDir>
#include <QDebug>
#include <QTimer>
#include <QSettings>
#include <QDomDocument>

xmlFileLoader::xmlFileLoader() :
    verbose(false),
    response(QByteArray()),
    cookie(""),
    page(""),

    reinitLoop(false),
    waitForHttp(false),
    waitInsertAt(0),
    waitHttp(QList<S_WAITFOR>()),
    waitingId(-1),

    m_fileSize(0),
    m_downloaded(0),
    m_progress(0),
    m_state(STATE_LOAD_XML),
    m_error(ERROR_NO_ERROR),
    m_mode(MODE_FREE),

    step(1),
    m_timeLeft(0),
    wait(0),
    timerStep(1),

    continueCheck(true),

    regExpResults(QMap<QString, QString>()),
    regExpListResults(QMap<QString, QStringList>()),

    id(0),
    m_logId(0),
    maxConnectionsPerDownloads(0),

    m_errorStr(""),
    url(QUrl("")),
    host(""),
    file(""),
    folder(""),
    downloadDir(QDir()),

    lastSize(0),
    m_speed(0),
    smooth(20),
    speedIntervall(500)
{
    m_downloadFile = new QFile();
    downloadDir = QDir::current();

    m_downloadStart = QDateTime::currentDateTime();
    downloadEnd = QDateTime::currentDateTime();

    speedList.clear();

    connect(&http, SIGNAL(requestFinished(int, bool)), this, SLOT(slot_http_requestFinished(int, bool)));
    connect(&http, SIGNAL(readyRead(const QHttpResponseHeader &)), this, SLOT(slot_http_readyRead(const QHttpResponseHeader &)));
    connect(&http, SIGNAL(dataReadProgress(int, int)), this, SLOT(slot_http_dataReadProgress(int, int)));
    connect(&http, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(slot_http_sslErrors(const QList<QSslError> &)));

    connect(&speedTimer, SIGNAL(timeout()), this, SLOT(slot_speedTimer_timeout()));
    connect(&waitTimer, SIGNAL(timeout()), this, SLOT(slot_waitTimer_timeout()));
}

xmlFileLoader::~xmlFileLoader()
{
    if(verbose) qDebug() << "~xmlFileLoader";

    disconnect(&http, 0, this, 0);
    disconnect(&speedTimer, 0, this, 0);
    disconnect(&waitTimer, 0, this, 0);

    waitTimer.stop();
    speedTimer.stop();
    http.abort();

    if(m_downloadFile)
    {
	if(m_downloadFile->isOpen())
	{
	    m_downloadFile->close();
	    m_downloadFile->deleteLater();
	} else
	{
	    delete m_downloadFile;
	}
    }
}

//http ->
void xmlFileLoader::slot_http_requestFinished(int requestId, bool httpError)
{
    //if(verbose) qDebug() << "slot_http_requestFinished" << waitingId << requestId;

    //nur reagieren, wenn auch beabsichtigt
    if(this->waitingId != requestId) return;

    //wenn obj geloescht werden soll, nicht mehr reagieren..
    if(m_state == STATE_DELETE)
    {
	if(m_downloadFile->isOpen()) m_downloadFile->close();
	this->m_errorStr = http.errorString();
	emit deleteMe(this->id, httpError);
	return;
    }

    if(httpError)
    {
        if(verbose) qDebug() << "slot_http_requestFinished, error:" << http.errorString();

        this->m_errorStr = "Error: " + http.errorString();
        handleError(ERROR_HTTP);
        return;
    } else
    {
        page = QString(response);

        if(verbose) qDebug() << "Page:" << page;

        if(waitForHttp)
        {
            waitForHttp = false;

            for(int i=0; i<waitHttp.count(); ++i)
            {
                waitInsertAt = 1;
                if(verbose) qDebug() << "do step after wait:" << waitHttp.at(i).node.nodeName();

                try
                {
                    doAction(waitHttp.at(i).node, waitHttp.at(i).it);
                } catch(xmlFileLoader::E_ERROR e)
                {
                    handleError(e);
                    return;
                }

                waitHttp.removeFirst();
                --i;

                if(waitInsertAt != 1)
                {
                    if(verbose) qDebug() << "should wait once again";
                    break;
                }
            }

        } else
        {
            QDomNodeList tmpNodeList = getDomNodeListByMode(m_mode);

            if(tmpNodeList.at(0).namedItem("download_order").hasChildNodes())
            {
                QDomNode download_order = tmpNodeList.at(0).namedItem("download_order");
                QDomNode stepX = download_order.childNodes().at(step-1);

                if(stepX.hasChildNodes())
                {
                    //alle moeglichen results durchgehen
                    QDomNodeList resultList = stepX.namedItem("result").childNodes();

                    for(int i=0; i<resultList.count(); ++i)
                    {
                        try
                        {
                            //wenn result zutrifft, fuehre alle actions aus
                            if(switchResult(resultList.at(i)))
                            {
                                if(resultList.at(i).hasChildNodes())
                                       doAction(resultList.at(i));

                                //nur ein result kann zutreffen
                                break;
                            }
                        } catch(xmlFileLoader::E_ERROR e)
                        {
                            handleError(e);
                            return;
                        }
                    }
                } else
                {
                    if(verbose) qDebug() << "step empty";
                }
            }
        }
    }
}

void xmlFileLoader::slot_http_readyRead(const QHttpResponseHeader & resp)
{
    Q_UNUSED(resp);
    response.append(http.readAll());
}

void xmlFileLoader::slot_http_dataReadProgress(int done, int total)
{
    if(m_state == STATE_DOWNLOADING)
    {
        m_downloaded = done;
        m_fileSize = total;

        if(m_fileSize > 0) m_progress = (int)((double)m_downloaded * 100.0 / (double)m_fileSize);
    }
}

void xmlFileLoader::slot_http_sslErrors(const QList<QSslError> &errors)
{
    if(verbose) qDebug() << "slot_http_sslErrors" << errors.size();
    http.ignoreSslErrors();
}
//<- http

//private ->

void xmlFileLoader::doAction(const QDomNode &parent, const int startI)
{
    if(waitForHttp)
    {
        if(verbose) qDebug() << "waiting for http";
        return;
    }

    if(verbose) qDebug() << "doAction, parent:" << parent.nodeName();

    int redoActionAt = 0;

    if(parent.hasChildNodes())
    {
        QDomNodeList actionList = parent.namedItem("action").childNodes();

        if(verbose) qDebug() << "doAction, count:" << actionList.count() << "start:" << startI;

        for(int i=startI; i<actionList.count(); ++i)
        {
            try
            {
                switchAction(actionList.at(i));
            } catch(xmlFileLoader::E_ERROR e)
            {
                throw e;
            }

            if(reinitLoop)
            {
                if(verbose) qDebug() << "reinitLoop was set in:" << actionList.at(i).nodeName();
                redoActionAt = i+1;
                break;
            } else if(waitForHttp)
            {
                if(verbose) qDebug() << "new wait:" << waitInsertAt;
                waitHttp.insert(waitInsertAt, S_WAITFOR(parent, i+1));
                waitInsertAt++;
                break;
            }
        }
    }

    if(reinitLoop)
    {
        if(verbose) qDebug() << "reinitLoop doaction" << reinitLoop;

        reinitLoop = false;
        doAction(parent, redoActionAt);
    }
}

int xmlFileLoader::parseType(const QString &type)
{
    if(type == "open_url")
        return TYPE_OPEN_URL;
    else if(type ==  "get_size")
        return TYPE_GET_SIZE;
    else if(type == "get")
        return TYPE_METHOD_GET;
    else if(type == "get_download")
        return TYPE_METHOD_GET_DOWNLOAD;
    else if(type == "post")
        return TYPE_METHOD_POST;
    else if(type == "post_download")
        return TYPE_METHOD_POST_DOWNLOAD;
    else if(type == "regexp")
        return TYPE_REGEXP;
    else if (type == "multi_regexp")
        return TYPE_MULTI_REGEXP;
    else if(type == "step")
        return TYPE_STEP;
    else if(type == "do_step")
        return TYPE_DO_STEP;
    else if(type == "halt")
        return TYPE_HALT;
    else if(type == "halt_login")
        return TYPE_HALT_LOGIN;
    else if(type == "halt_get_size")
        return TYPE_HALT_GET_SIZE;
    else if(type == "wait")
        return TYPE_WAIT;
    else if(type == "prepare_download")
        return TYPE_PREPARE_DOWNLOAD;
    else if(type == "finish_download")
        return TYPE_FINISH_DOWNLOAD;
    else if(type == "check_file")
        return TYPE_CHECK_FILE;
    else if(type == "select_mirror")
        return TYPE_SELECT_MIRROR;
    else if(type == "check_size")
        return TYPE_CHECK_SIZE;
    else if(type == "check_content")
        return TYPE_CHECK_CONTENT;
    else if(type == "dummy")
        return TYPE_DUMMY;
    else if(type == "delete_file")
        return TYPE_DELETE_FILE;
    else if(type == "set_state")
        return TYPE_SET_STATE;
    else if(type == "break_check")
        return TYPE_BREAK_CHECK;
    else if(type == "set_cookie")
        return TYPE_SET_COOKIE;
    else if(type == "save_cookie")
        return TYPE_SAVE_COOKIE;
    else if(type == "load_settings")
        return TYPE_LOAD_SETTINGS;
    else if(type == "header")
        return TYPE_HEADER;
    else if(type == "wait_http")
        return TYPE_WAIT_HTTP;
    else if(type == "mode")
        return TYPE_MODE;
    else if(type == "bool_fct")
        return TYPE_BOOL_FCT;
    else if(type == "set_error")
        return TYPE_SET_ERROR;
    else if(type == "emit_state")
        return TYPE_EMIT_STATE;
    else
        return TYPE_NO_FOUND;
}

int xmlFileLoader::parseState(const QString &state)
{
	if(state == "running")
		return STATE_RUNNING;
	else if(state == "finished")
		return STATE_FINISHED;
	else if(state == "waiting")
		return STATE_WAITING;
	else if(state == "downloading")
		return STATE_DOWNLOADING;
	else
		return -1;
}

bool xmlFileLoader::switchResult(const QDomNode &result)
{
    if(verbose) qDebug() << "result" << result.nodeName() << result.attributes().namedItem("type").nodeValue();

    checkAttributes(result, QStringList() << "type");

    switch(parseType(result.attributes().namedItem("type").nodeValue()))
    {
	default:

	    this->m_errorStr = tr("Error: xml_result not found");
	    throw ERROR_XML;
	    break;

	case TYPE_REGEXP:
	{
	    checkAttributes(result, QStringList() << "regexp");

	    QRegExp tmpRegExp(result.attributes().namedItem("regexp").nodeValue());

	    if(tmpRegExp.indexIn(page) != -1)
	    {
		//wenn var vorhanden, alle vars in regExpResults speichern
		if(result.hasChildNodes())
		{
		    QDomNamedNodeMap varList = result.namedItem("var").attributes();

		    if(varList.count() == tmpRegExp.numCaptures())
		    {
			for(int i=0; i<varList.count(); ++i)
			    addVar(varList.item(i).nodeName(), tmpRegExp.cap(varList.item(i).nodeValue().toInt()));

		    } else
		    {
			if(verbose) qDebug() << "error: wrong var number";

			this->m_error = ERROR_XML;
			this->m_errorStr = tr("Error in xml file, to many var for regexp given");

			throw ERROR_XML;
		    }
		}

		return true;
	    } else
	    {
		return false;
	    }
	    break;
	}

	case TYPE_CHECK_SIZE:
	{
	    bool tmpReturn = true;

	    checkAttributes(result, QStringList() << "max_size");

	    if(m_downloadFile && m_downloadFile->exists())
	    {
		if(m_downloadFile->size() > result.attributes().namedItem("max_size").nodeValue().toInt())
		    tmpReturn = false;
	    } else
	    {
		tmpReturn = false;
	    }

	    return tmpReturn;
	    break;
	}

	case TYPE_CHECK_CONTENT:
	{
	    checkAttributes(result, QStringList() << "regexp");

	    QRegExp tmpRegExp(result.attributes().namedItem("regexp").nodeValue());
	    QString tmpStr = "";
	    QFile tmpFile(downloadDir.filePath(file));

	    if(tmpFile.exists())
	    {
		if(!tmpFile.open(QIODevice::ReadOnly | QIODevice::Text)) return false;

		tmpStr = tmpFile.readAll();
		tmpFile.close();

		if(tmpRegExp.indexIn(tmpStr) != -1) return true;

	    } else
	    {
		    return false;
	    }

	    return false;
	    break;
	}

	case TYPE_DUMMY:
	{
	    //ueberpruefen, ob regexp gesetzt ist
	    if(!result.attributes().contains("result"))
	    {
		emit statusMsg(m_logId, "Warning in xml file, result not given");
		return true;
	    } else
	    {
		if(result.attributes().namedItem("result").nodeValue().toInt() == 1)
		{
		    return true;
		} else
		{
		    return false;
		}
	    }

	    break;
	}

	case TYPE_HEADER:
	{
	    if(verbose) qDebug() << http.lastResponse().keys().join("|");

	    checkAttributes(result, QStringList() << "key" << "save");

	    if(http.lastResponse().hasKey(result.attributes().namedItem("key").nodeValue()))
	    {
		addVar(result.attributes().namedItem("save").nodeValue(), http.lastResponse().value(result.attributes().namedItem("key").nodeValue()));

		return true;
	    } else
	    {
		return false;
	    }

	    break;
	}
    }

    return false;
}

void xmlFileLoader::switchAction(const QDomNode &action)
{
    if(verbose) qDebug() << "action" << action.nodeName() << action.attributes().namedItem("type").nodeValue();

    checkAttributes(action, QStringList() << "type");

    switch(parseType(action.attributes().namedItem("type").nodeValue()))
    {
	default:

	    this->m_errorStr = tr("Error: xml, unknown action: ") + QString("%1, %2").arg(step).arg(parseType(action.attributes().namedItem("type").nodeValue())) + " : " + action.nodeName() + " = " + action.attributes().namedItem("type").nodeValue();
	    throw ERROR_XML;
	    break;

	case TYPE_OPEN_URL:
	{
	    QUrl tmpUrl;
	    response = QByteArray();

	    if(action.attributes().contains("url"))
	    {
		QString tmpUrlStr = getVar(action.attributes().namedItem("url").nodeValue());
		tmpUrl = QUrl(tmpUrlStr);
	    } else
	    {
		tmpUrl = url;
	    }

	    checkAttributes(action, QStringList() << "method");

	    //pruefen, welche methode gewahelt werden soll
	    switch(parseType(action.attributes().namedItem("method").nodeValue()))
	    {
		default:

		    this->m_errorStr = tr("Error in xml: unknown method in open_url") + action.attributes().namedItem("method").nodeValue();
		    throw ERROR_XML;
		    break;

		case TYPE_METHOD_GET:
		{
		    if(verbose) qDebug() << "GET:" << tmpUrl.host() << tmpUrl.path();

		    QHttpRequestHeader header("GET", tmpUrl.path());
		    header.setValue("Host", tmpUrl.host());

		    if(!cookie.isEmpty()) header.setValue("Cookie", cookie);

		    if(tmpUrl.toString().left(5) == "https")
		    {
			http.setHost(tmpUrl.host(), QHttp::ConnectionModeHttps);
		    } else
		    {
			http.setHost(tmpUrl.host());
		    }

		    waitingId = http.request(header, QByteArray());
		    break;
		}

		case TYPE_METHOD_GET_DOWNLOAD:
		{
		    if(verbose) qDebug() << "GET_DOWNLOAD:" << tmpUrl.host() << tmpUrl.path();

		    emit statusMsg(m_logId, "Starting download.");
		    speedTimer.start(speedIntervall);
		    m_progress = 0;
		    m_downloadStart = QDateTime::currentDateTime();

		    QHttpRequestHeader header("GET", tmpUrl.path());
		    header.setValue("Host", tmpUrl.host());

		    if(!cookie.isEmpty()) header.setValue("Cookie", cookie);

		    if(tmpUrl.toString().left(5) == "https")
		    {
			http.setHost(tmpUrl.host(), QHttp::ConnectionModeHttps);
		    } else
		    {
			http.setHost(tmpUrl.host());
		    }

		    waitingId = http.request(header, QByteArray(), m_downloadFile);
		    break;
		}

		case TYPE_METHOD_POST:
		{
		    QByteArray post = "";

		    QDomNamedNodeMap postData = action.namedItem("post_data").attributes();
		    for(int i=0; i<postData.count(); ++i)
		    {
			if(i > 0) post.append("&");
			post.append(postData.item(i).nodeName() + "=" + QUrl::toPercentEncoding(getVar(postData.item(i).nodeValue())));
		    }

		    QHttpRequestHeader header("POST", tmpUrl.path());
		    header.setValue("Host", tmpUrl.host());

		    //header.setValue("Content-Type", "application/x-www-form-urlencoded");
		    QDomNamedNodeMap headerData = action.namedItem("header_data").attributes();
		    for(int i=0; i<headerData.count(); ++i)
		    {
			header.setValue(headerData.item(i).nodeName(), getVar(headerData.item(i).nodeValue()));
		    }

		    if(!cookie.isEmpty()) header.setValue("Cookie", cookie);

		    if(verbose) qDebug() << "POST:" << tmpUrl.host() << tmpUrl.path() << "post:" << post << "header:" << header.keys().join("|");

		    if(tmpUrl.toString().left(5) == "https")
		    {
			http.setHost(tmpUrl.host(), QHttp::ConnectionModeHttps);
		    } else
		    {
			http.setHost(tmpUrl.host());
		    }

		    waitingId = http.request(header, post);

		    break;
		}

		case TYPE_METHOD_POST_DOWNLOAD:
		{
		    QByteArray post = "";

		    QDomNamedNodeMap postData = action.namedItem("post_data").attributes();
		    for(int i=0; i<postData.count(); ++i)
		    {
			    if(i > 0) post.append("&");
			    post.append(postData.item(i).nodeName() + "=" + postData.item(i).nodeValue());
		    }

		    if(verbose) qDebug() << "POST_DOWNLOAD:" << tmpUrl.host() << tmpUrl.path() << post;

		    emit statusMsg(m_logId, "Starting download.");
		    speedTimer.start(speedIntervall);
		    m_progress = 0;
		    m_downloadStart = QDateTime::currentDateTime();

		    QHttpRequestHeader header("POST", tmpUrl.path());
		    header.setValue("Host", tmpUrl.host());

		    if(!cookie.isEmpty()) header.setValue("Cookie", cookie);

		    if(tmpUrl.toString().left(5) == "https")
		    {
			    http.setHost(tmpUrl.host(), QHttp::ConnectionModeHttps);
		    } else
		    {
			    http.setHost(tmpUrl.host());
		    }

		    waitingId = http.request(header, post, m_downloadFile);
		    break;
		}
	    }

	    break;
	}

	case TYPE_GET_SIZE:
	{
	    checkAttributes(action, QStringList() << "size" << "order");

	    QStringList sizeNames = QStringList() << " " << "K" << "M" << "G" << "T" << "P";
	    for(int i=0; i<sizeNames.count(); ++i)
	    {
		if(getVar(action.attributes().namedItem("order").nodeValue()) == sizeNames.at(i))
		{
		    double tmpDbl = getVar(action.attributes().namedItem("size").nodeValue()).toDouble();

		    double tmpMultipl = getVar("$multiplyer").toDouble();
		    if(tmpMultipl <= 0) tmpMultipl = 1000;

		    for(int o=0; o<i; ++o)
			tmpDbl = tmpDbl * tmpMultipl;

		    m_fileSize = tmpDbl;
		    return;
		}
	    }

	    break;
	}

	case TYPE_STEP:
	{
	    checkAttributes(action, QStringList() << "step");

	    step = action.attributes().namedItem("step").nodeValue().toInt();
	    break;
	}

	case TYPE_DO_STEP:
	{
	    checkAttributes(action, QStringList() << "step");

	    step = action.attributes().namedItem("step").nodeValue().toInt();

	    doStep();

	    break;
	}

	case TYPE_HALT:
	{
	    checkAttributes(action, QStringList() << "error");

	    if(getVar(action.attributes().namedItem("error").nodeValue()).toInt() == 1)
	    {
		this->m_errorStr = tr("Error, in step: %1").arg(action.attributes().namedItem("step").nodeValue().toInt());
		throw ERROR_XML;
	    } else
	    {
		this->m_state = STATE_STOPPED;
		throw ERROR_NO_ERROR;
	    }
	    break;
	}

	case TYPE_HALT_LOGIN:
	{
	    checkAttributes(action, QStringList() << "error");

	    if(getVar(action.attributes().namedItem("error").nodeValue()).toInt() == 1)
	    {
		this->m_errorStr = tr("Error, in step: %1").arg(action.attributes().namedItem("step").nodeValue().toInt());
		throw ERROR_LOGIN;
	    } else
	    {
		emit loginFinished(id, false);

		m_state = STATE_STOPPED;
		throw ERROR_NO_ERROR;
	    }

	    break;
	}

        case TYPE_HALT_GET_SIZE:
        {
            checkAttributes(action, QStringList() << "error");

            if(getVar(action.attributes().namedItem("error").nodeValue()).toInt() == 1)
            {
		this->m_errorStr = tr("Error, in step: %1").arg(action.attributes().namedItem("step").nodeValue().toInt());
                throw ERROR_GET_SIZE;
            } else
            {
                emit getSizeFinished(id, false);
                m_state = STATE_STOPPED;
                throw ERROR_NO_ERROR;
            }
            break;
        }

        case TYPE_WAIT:
        {
            checkAttributes(action, QStringList() << "wait" << "multiplyer" << "step");

            int tmpWait = getVar(action.attributes().namedItem("wait").nodeValue()).toInt();

            int tmpMultipl = action.attributes().namedItem("multiplyer").nodeValue().toInt();
            int tmpStep = action.attributes().namedItem("step").nodeValue().toInt();

            m_timeLeft = tmpWait*tmpMultipl;
            wait = m_timeLeft;
            timerStep = tmpStep;

            waitTimer.start(1000);

            break;
        }

        case TYPE_REGEXP:
        {
            checkAttributes(action, QStringList() << "regexp");

            QRegExp tmpRegExp(action.attributes().namedItem("regexp").nodeValue());

            if(tmpRegExp.indexIn(page) != -1)
            {
                //wenn var vorhanden, alle vars in regExpResults speichern
                if(action.hasChildNodes())
                {
                    QDomNamedNodeMap varList = action.namedItem("var").attributes();

                    if(varList.count() == tmpRegExp.numCaptures())
                    {
                        for(int i=0; i<varList.count(); ++i)
                            addVar(varList.item(i).nodeName(), tmpRegExp.cap(varList.item(i).nodeValue().toInt()));
                    } else
                    {
                        if(verbose) qDebug() << "error: wrong var number";

			this->m_errorStr = tr("Error in xml file, to many var for regexp given");
                        throw ERROR_XML;
                        return;
                    }
                }
            }
            break;
        }

        case TYPE_MULTI_REGEXP:
        {
            checkAttributes(action, QStringList() << "regexp");

            QRegExp tmpRegExp(action.attributes().namedItem("regexp").nodeValue());

            if(tmpRegExp.indexIn(page) != -1)
            {
                if(action.hasChildNodes())
                {
                    QStringList tmpStrList;
                    tmpRegExp.setMinimal(true);
                    int pos = 0;

                    QDomNamedNodeMap varList = action.namedItem("var").attributes();

                    QList<QStringList> tmpList;

                    while((pos = tmpRegExp.indexIn(page, pos)) != -1)
                    {
                        QStringList tmpCapStrList;
                        for(int i=0; i<varList.count(); ++i)
                            tmpCapStrList.append(tmpRegExp.cap(varList.item(i).nodeValue().toInt()));

                        tmpList.append(tmpCapStrList);

                        pos += tmpRegExp.matchedLength();
                    }

                    for(int i=0; i<varList.count(); ++i)
                    {
                        QStringList tmpVarStrList;
                        for(int o=0; o<tmpList.count(); ++o)
                            tmpVarStrList.append(tmpList.at(o).at(i));

                        regExpListResults.insert(varList.item(i).nodeName(), tmpVarStrList);
                        if(verbose) qDebug() << "regExpListResults:" << varList.item(i).nodeName() << tmpVarStrList.join("|");
                    }
                }
            }

            break;
        }

        case TYPE_PREPARE_DOWNLOAD:
        {
            if(!downloadDir.exists(folder))
                downloadDir.mkdir(folder);

            downloadDir.cd(folder);

            while(downloadDir.exists(file))
            {
                emit statusMsg(m_logId, "file exists, renaming.");
                file = "_" + file;
            }

            m_downloadFile = new QFile(downloadDir.filePath(file));
            if(!m_downloadFile->open(QIODevice::WriteOnly))
            {
		this->m_errorStr = tr("Cant open file.");
                throw ERROR_FILE;
                return;
            }

            break;
        }

        case TYPE_SELECT_MIRROR:
        {
            checkAttributes(action, QStringList() << "list" << "use");

            QStringList tmpListStr = getListVar(action.attributes().namedItem("list").nodeValue());

            addVar(action.attributes().namedItem("use").nodeValue(), tmpListStr.at(0));

            break;
        }

        case TYPE_FINISH_DOWNLOAD:
        {
            m_downloadFile->close();
            speedTimer.stop();
            downloadEnd = QDateTime::currentDateTime();

            throw ERROR_NO_ERROR;
            break;
        }

        case TYPE_BREAK_CHECK:
        {
            continueCheck = false;
            break;
        }

        case TYPE_CHECK_FILE:
        {
            continueCheck = true;

            checkAttributes(action, QStringList() << "check");

            QDomNodeList checks = action.namedItem("check").childNodes();
            for(int i=0; i<checks.count(); ++i)
            {
                //checks koennen unterbrochen werden durch action=break_check
                if(!continueCheck) break;

                //checks sind results aber haben andere reihenfolge
                //wenn result zutrifft, fuehre actions in true aus, sonst die in false
                //wenn weder true noch false gegeben, brauchst erst gar nich guggen
                if(checks.at(i).hasChildNodes())
                {
                    if(switchResult(checks.at(i)))
                    {
                        if(checks.at(i).namedItem("true").hasChildNodes())
                        {
                            if(verbose) qDebug() << "checkfile doaction";
                            doAction(checks.at(i).namedItem("true"));
                        }
                    } else
                    {
                        if(checks.at(i).namedItem("false").hasChildNodes())
                        {
                            if(verbose) qDebug() << "checkfile doaction";
                            doAction(checks.at(i).namedItem("false"));
                        }
                    }
                }
            }

            break;
        }

        case TYPE_DELETE_FILE:
        {
            if(m_downloadFile->exists())
            {
                m_downloadFile->remove();
            }

            break;
        }

        case TYPE_SET_STATE:
        {
            checkAttributes(action, QStringList() << "state");

            int tmpState = parseState(action.attributes().namedItem("state").nodeValue());
            if(tmpState >= 0)
            {
                m_state = E_STATE(tmpState);
            } else
            {
		this->m_errorStr = tr("Error in xml file, state not valid");
                throw ERROR_XML;
                return;
            }

            break;
        }

        case TYPE_LOAD_SETTINGS:
        {
            if(action.hasChildNodes())
            {
                QDomNamedNodeMap varList = action.namedItem("var").attributes();

                QSettings settings("raptor-loader", "Raptor");

                for(int i=0; i<varList.count(); ++i)
                    addVar(varList.item(i).nodeValue(), settings.value("hoster/" + host + "/settings/" + varList.item(i).nodeName(), "not found").toString());
            }
                break;
        }

        case TYPE_SAVE_COOKIE:
        {
            checkAttributes(action, QStringList() << "cookie");

            QStringList cookieParams = QStringList() << "Comment" << "Domain" << "Max-Age" << "Path" << "Secure" << "Version" << "Expires";

            QSettings settings("raptor-loader", "Raptor");

            QStringList tmpList = getVar(action.attributes().namedItem("cookie").nodeValue()).split(";");

            for(int i=0; i<tmpList.size(); ++i)
            {
                QStringList tmpList2 = tmpList.at(i).split("=");

                if(tmpList2.size() == 2 && !cookieParams.contains(tmpList2.at(0).trimmed(), Qt::CaseInsensitive)) settings.setValue("hoster/" + host + "/settings/" + tmpList2.at(0).trimmed(), tmpList2.at(1).trimmed());
            }

            break;
        }

        case TYPE_SET_COOKIE:
        {
            QStringList tmpCookie = QStringList();

            if(action.hasChildNodes())
            {
                QDomNamedNodeMap varList = action.namedItem("var").attributes();

                for(int i=0; i<varList.count(); ++i)
                {
                    tmpCookie.append(varList.item(i).nodeName() + "=" + getVar(varList.item(i).nodeValue()));
                }
            }

            cookie = tmpCookie.join("&");

            if(verbose) qDebug() << "cookie:" << cookie;
            break;
        }

        case TYPE_HEADER:
        {
            checkAttributes(action, QStringList() << "key" << "save");

            qDebug() << "header:" << http.lastResponse().keys().join("|");

            if(http.lastResponse().hasKey(action.attributes().namedItem("key").nodeValue()))
            {
                if(verbose) qDebug() << "header:" << http.lastResponse().value(action.attributes().namedItem("key").nodeValue());
                addVar(action.attributes().namedItem("save").nodeValue(), http.lastResponse().value(action.attributes().namedItem("key").nodeValue()));
            } else
            {
		this->m_errorStr = tr("Error in xml file, key not in header");
                throw ERROR_XML;
                return;
            }

            break;
        }

        case TYPE_WAIT_HTTP:
        {
            waitForHttp = true;
            break;
        }

        case TYPE_MODE:
        {
            checkAttributes(action, QStringList() << "mode");

            if(action.attributes().namedItem("mode").nodeValue() == "premium")
                emit changeMode(id, MODE_PREMIUM);
            else
                emit changeMode(id, MODE_FREE);

            break;
        }

        case TYPE_BOOL_FCT:
        {
            checkAttributes(action, QStringList() << "function" << "save");

            bool tmpBool = true;

            if(action.attributes().namedItem("function").nodeValue() == "isEmpty")
            {
                checkAttributes(action, QStringList() << "input1");

                tmpBool = getVar(action.attributes().namedItem("input1").nodeValue()).isEmpty();

                addVar(action.attributes().namedItem("save").nodeValue(), tmpBool ? "1": "0");
            }

            break;
        }

        case TYPE_SET_ERROR:
        {
            checkAttributes(action, QStringList() << "error");

	    this->m_errorStr = getVar(action.attributes().namedItem("error").nodeValue());
            throw ERROR_XML;
            break;
        }

        case TYPE_EMIT_STATE:
        {
            checkAttributes(action, QStringList() << "msg");

            emit statusMsg(m_logId, getVar(action.attributes().namedItem("msg").nodeValue()));
            break;
        }
    }
}

void xmlFileLoader::addVar(const QString &key, const QString &value)
{
	regExpResults.insert(key, value);
	if(verbose) qDebug() << "addVar:" << key << value;
}

QString xmlFileLoader::getVar(const QString &var)
{
    if(verbose) qDebug() << "getVar:" << var;

    if(var == "$URL") return url.toString();

    QString tmpStr = var;

    if(tmpStr.left(1) == "$")
    {
        if(regExpResults.contains(tmpStr.mid(1)))
        {
            tmpStr = regExpResults.value(tmpStr.mid(1));
        } else
        {
            if(verbose) qDebug() << "var" << var << "doesnt exist";

	    this->m_errorStr = tr("Error in xml file, var not present");
            throw ERROR_GET_VAR;
        }
    }

    return tmpStr;
}

QStringList xmlFileLoader::getListVar(const QString &var)
{
    if(verbose) qDebug() << "getListVar:" << var;

    QStringList tmpStrList;

    if(var.left(1) == "$")
    {
        if(regExpListResults.contains(var.mid(1)))
        {
            tmpStrList = regExpListResults.value(var.mid(1));
        } else
        {
            if(verbose) qDebug() << "var" << var << "doesnt exist";

	    this->m_errorStr = tr("Error in xml file, var not present");
            throw ERROR_GET_VAR;
        }
    } else
    {
        tmpStrList = var.split("|");
    }

    return tmpStrList;
}

void xmlFileLoader::checkAttributes(const QDomNode &node, const QStringList &attributes)
{
    for(int i=0; i<attributes.count(); ++i)
    {
	if(!node.attributes().contains(attributes.at(i)))
	{
	    if(verbose) qDebug() << "checkAttributes: found Error";

	    this->m_state = STATE_ERROR;
	    this->m_error = ERROR_XML;

	    this->m_errorStr = tr("Error in xml file, attribute %1 not present").arg(attributes.at(i));

	    throw ERROR_ATTR;
	}
    }
}

void xmlFileLoader::doStep()
{
    QDomNodeList tmpNodeList = getDomNodeListByMode(m_mode);

    if(tmpNodeList.at(0).namedItem("download_order").hasChildNodes())
    {
        QDomNode download_order = tmpNodeList.at(0).namedItem("download_order");
        QDomNode stepX = download_order.childNodes().at(step-1);

        if(stepX.hasChildNodes())
        {
            try
            {
                doAction(stepX);
            } catch(xmlFileLoader::E_ERROR e)
            {
                throw e;
            }
        }
    }
}

void xmlFileLoader::loadVar()
{
    QDomNodeList tmpNodeList = domHoster.elementsByTagName("var");

    if(tmpNodeList.at(0).hasAttributes())
    {
        for(int i=0; i<tmpNodeList.at(0).attributes().size(); ++i)
        {
            addVar(tmpNodeList.at(0).attributes().item(i).nodeName(), tmpNodeList.at(0).attributes().item(i).nodeValue());
        }
    }
}

bool xmlFileLoader::loadXmlFile(const QString &xmlFile)
{
    QFile file(xmlFile);
    domHoster = QDomDocument("hoster");

    if(!file.open(QIODevice::ReadOnly)) return false;

    if(!domHoster.setContent(&file))
    {
        file.close();
        return false;
    }

    m_state = STATE_STOPPED;
    loadVar();
    file.close();
    return true;
}

void xmlFileLoader::setSpeed(const double &newSpeed)
{
	if(!speedList.isEmpty() && speedList.count() == smooth)
	{
		speedList.removeFirst();
		speedList.append(newSpeed);
	} else
	{
		speedList.append(newSpeed);
	}

	double tmpDbl = 0;

	for(int i=0; i<speedList.size(); ++i)
	{
		tmpDbl += speedList.at(i);
	}

	if(speedList.isEmpty())
		m_speed = 0;
	else
		m_speed = (tmpDbl / speedList.size());

	if(m_speed < 1) m_speed = 0;
}

void xmlFileLoader::handleError(int const& e)
{
    switch(e)
    {
        default:
        case ERROR_XML:

            this->m_state = STATE_ERROR;
            this->m_error = ERROR_XML;

            emit downloadFinished(this->id, true);
            break;

        case ERROR_NO_ERROR:

            this->m_error = ERROR_NO_ERROR;
            emit downloadFinished(this->id, false);
            break;

        case ERROR_GET_SIZE:

            this->m_error = ERROR_GET_SIZE;
            emit getSizeFinished(id, true);
            break;

        case ERROR_LOGIN:

            this->m_error = ERROR_LOGIN;
            emit loginFinished(id, true);
            break;

        case ERROR_FILE:

            this->m_error = ERROR_FILE;
            break;

        case ERROR_HTTP:

            this->m_error = ERROR_HTTP;
            emit downloadFinished(id, true);
            break;
    }
}


QDomNodeList xmlFileLoader::getDomNodeListByMode(xmlFileLoader::E_MODE const& _mode)
{
    switch(_mode)
    {
        default:
        case MODE_FREE:
            return domHoster.elementsByTagName("free");
            break;

        case MODE_PREMIUM:
            return domHoster.elementsByTagName("premium");
            break;

        case MODE_LOGIN:
            return domHoster.elementsByTagName("login");
            break;

        case MODE_GET_SIZE:
            return domHoster.elementsByTagName("get_size");
            break;
    }
}
//<- private

//public ->
bool xmlFileLoader::isValidHoster(const QString &host)
{
    QDomNodeList tmpNodeList = domHoster.elementsByTagName("url_regexp");

    if(!tmpNodeList.isEmpty())
    {
        if(tmpNodeList.at(0).attributes().contains("host"))
        {
            if(host.indexOf(QRegExp(tmpNodeList.at(0).attributes().namedItem("host").nodeValue())) != -1)
            {
                return true;
            } else
            {
                return false;
            }
        }
    }

    return false;
}

bool xmlFileLoader::setLink(const QUrl &newUrl)
{
	if(isValidHoster(newUrl.host()))
	{
		bool tmpBool = true;

		if(domHoster.childNodes().at(0).attributes().contains("host"))
			host = domHoster.childNodes().at(0).attributes().namedItem("host").nodeValue();
		else
			host = newUrl.host();

		if(domHoster.childNodes().at(0).attributes().contains("allowOtherHostnames"))
			tmpBool = domHoster.childNodes().at(0).attributes().namedItem("allowOtherHostnames").nodeValue() == "0" ? false: true;

		url = newUrl;
		if(!tmpBool) url.setHost(host);

		return true;
	} else
	{
		return false;
	}
}

void xmlFileLoader::setFolder(const QString &newFolder)
{
    this->folder = newFolder;
}

void xmlFileLoader::setDownloadDir(const QString &newFolder)
{
    this->downloadDir = QDir(newFolder);
}

void xmlFileLoader::setFile(const QString &newFile)
{
    this->file = newFile;
}

void xmlFileLoader::setLogId(const int &newId)
{
    this->m_logId = newId;
}

void xmlFileLoader::setId(const int &newId)
{
    this->id = newId;
}

void xmlFileLoader::setMode(const E_MODE &newMode)
{
    this->m_mode = newMode;
}

int xmlFileLoader::setProxy(QNetworkProxy const &proxy)
{
    return http.setProxy(proxy);
}

void xmlFileLoader::start()
{
    if(verbose) qDebug() << "START" << reinitLoop;

    m_downloadStart = QDateTime::currentDateTime();
    downloadEnd = QDateTime::currentDateTime();

    try
    {
        doStep();
    } catch(xmlFileLoader::E_ERROR e)
    {
        handleError(e);
    }
}

void xmlFileLoader::loadXml(const QString &xmlFile)
{
    if(!loadXmlFile(xmlFile))
    {
        if(verbose) qDebug() << "error in xml file";

        this->m_error = ERROR_XML;
        this->m_errorStr = tr("Can't load xml file.");
        this->m_state = STATE_ERROR;

        emit downloadFinished(id, true);
    }
}

int xmlFileLoader::state()
{
    return this->m_state;
}

int xmlFileLoader::error()
{
    return this->m_error;
}

int xmlFileLoader::progress()
{
    return this->m_progress;
}

double xmlFileLoader::speed()
{
    return this->m_speed;
}

int xmlFileLoader::timeLeft()
{
    return this->m_timeLeft;
}

int xmlFileLoader::logId()
{
    return this->m_logId;
}

QPointer<QFile> xmlFileLoader::downloadFile()
{
    return this->m_downloadFile;
}

QString xmlFileLoader::link()
{
    return this->url.toString();
}

QDateTime xmlFileLoader::downloadStart()
{
    return this->m_downloadStart;
}

uint xmlFileLoader::fileSize()
{
    return this->m_fileSize;
}

int xmlFileLoader::mode()
{
    return this->m_mode;
}

uint xmlFileLoader::downloaded()
{
    return this->m_downloaded;
}

void xmlFileLoader::stop()
{
    if(m_state != STATE_STOPPED)
    {
	this->m_state = STATE_DELETE;
	http.abort();
    }
}

void xmlFileLoader::login(const QString &user, const QString &pass)
{
    setMode(MODE_LOGIN);

    addVar("username", user);
    addVar("password", pass);

    try
    {
        doStep();
    } catch(xmlFileLoader::E_ERROR e)
    {
        handleError(e);
    }
}

void xmlFileLoader::getSize()
{
    setMode(MODE_GET_SIZE);

    try
    {
        doStep();
    } catch(xmlFileLoader::E_ERROR e)
    {
        handleError(e);
    }
}

QString xmlFileLoader::errorStr()
{
    return this->m_errorStr;
}
//<-public

//slots ->
void xmlFileLoader::slot_waitTimer_timeout()
{
    if(m_state == STATE_STOPPED) return;

    --m_timeLeft;

    if(wait > 0) m_progress = (int)(100 * ((double)(wait - m_timeLeft) / wait));

    if(m_timeLeft <= 0)
    {
        waitTimer.stop();

        step = timerStep;

        try
        {
            doStep();
        } catch(xmlFileLoader::E_ERROR e)
        {
            handleError(e);
        }
    }
}
//<- slots

//speedTimer
void xmlFileLoader::slot_speedTimer_timeout()
{
    if(speedTimer.interval() > 0) setSpeed((m_downloaded - lastSize) / ((double)speedTimer.interval() / 1000.0));

    lastSize = m_downloaded;

    if(m_speed > 0) m_timeLeft = (int)((double)(m_fileSize - m_downloaded) / m_speed);
}
//<- speedTimer
